<?php
/**
 * MyFileValidator class file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright &copy; 2008-2011 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

/**
 * CFileValidator verifies if an attribute is receiving a valid uploaded file.
 *
 * It uses the model class and attribute name to retrieve the information
 * about the uploaded file. It then checks if a file is uploaded successfully,
 * if the file size is within the limit and if the file type is allowed.
 *
 * This validator will attempt to fetch uploaded data if attribute is not
 * previously set. Please note that this cannot be done if input is tabular:
 * <pre>
 *  foreach($models as $i=>$model)
 *     $model->attribute = CUploadedFile::getInstance($model, "[$i]attribute");
 * </pre>
 * Please note that you must use {@link CUploadedFile::getInstances} for multiple
 * file uploads.
 *
 * When using CFileValidator with an active record, the following code is often used:
 * <pre>
 *  if($model->save())
 *  {
 *     // single upload
 *     $model->attribute->saveAs($path);
 *     // multiple upload
 *     foreach($model->attribute as $file)
 *        $file->saveAs($path);
 *  }
 * </pre>
 *
 * You can use {@link CFileValidator} to validate the file attribute.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: CFileValidator.php 2799 2011-01-01 19:31:13Z qiang.xue $
 * @package system.validators
 * @since 1.0
 */
class MyFileValidator extends CFileValidator
{

	/**
	 * Internally validates a file object.
	 * @param CModel $object the object being validated
	 * @param string $attribute the attribute being validated
	 * @param CUploadedFile $file uploaded file passed to check against a set of rules
	 */
	protected function validateFile($object, $attribute, $file)
	{
		if(null===$file || ($error=$file->getError())==UPLOAD_ERR_NO_FILE)
			return $this->emptyAttribute($object, $attribute);
		else if($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize)
		{
			$message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file "{file}" is too large. Its size cannot exceed {limit} bytes.');
			$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->getSizeLimit()));
		}
		else if($error==UPLOAD_ERR_PARTIAL)
			throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>$file->getName())));
		else if($error==UPLOAD_ERR_NO_TMP_DIR)
			throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>$file->getName())));
		else if($error==UPLOAD_ERR_CANT_WRITE)
			throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>$file->getName())));
		else if(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION)  // available for PHP 5.2.0 or above
			throw new CException(Yii::t('yii','File upload was stopped by extension.'));

		if($this->minSize!==null && $file->getSize()<$this->minSize)
		{
			$message=$this->tooSmall!==null?$this->tooSmall : Yii::t('yii','The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.');
			$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->minSize));
		}
		if($this->types!==null)
		{
			if(is_string($this->types))
				$types=preg_split('/[\s,]+/',strtolower($this->types),-1,PREG_SPLIT_NO_EMPTY);
			else
				$types=$this->types;

			$contentType = $this->getFileType($file);
			if($contentType === false || count(array_intersect($contentType, $types))<=0){
				$message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.');
				$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types)));
			}

// 			if(!in_array(strtolower($file->getExtensionName()),$types))
// 			{
// 				$message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.');
// 				$this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types)));
// 			}
		}

	}

	function getFileType($file){
		if(empty($file->tempName)) return false;
// 		$type = mime_content_type($file->tempName);
		$type = CFileHelper::getMimeType($file->tempName);
		
		switch($type){
			case 'application/pdf':
				return array('pdf');
				break;
			case 'image/gif';
				return array('gif');
				break;

			case 'image/png':
				return array('png');
				break;
				
			case 'image/jpeg':
				return array('jpeg','jpg','jpe');
				break;
				
			case 'audio/mpeg':
			case 'audio/mpg':
				return array('mp3');
				break;

			case 'audio/x-wav':
				return array('wav');
				break;
				
			case 'video/quicktime':
				return array('mov','qt');
				break;

			case 'video/x-msvideo':
				return array('avi');
				break;

			case 'video/x-flv':
				return array('flv');
				break;

			case 'video/3gpp':
				return array('3gpp','3gp');
				break;
				
			case 'video/mp4':
				return array('mp4');
				break;

			case 'video/x-ms-wmv':
				return array('wmv');
				break;
				
			case 'video/ogg':
			case 'application/ogg':
				return array('ogv');
				break;
			
			case 'video/mpeg':
				return array('mpg','mpeg');
				break;
			
			case 'application/msword':
				return array('doc');
				break;

			case 'application/vnd.ms-office':
				return array('doc','docx');
				break;

			case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
				return array('docx');
				break;

			case 'application/octet-stream':
				return array('docx','xlsx');
				break;
			case 'application/zip':
			case 'application/x-zip':
			case 'application/x-zip-compressed':
				$check_source  = $this->readZippedXML($file->tempName,$file->name);
				if(!empty($check_source))
					return array("pptx","docx","xlsx");
				return false;
				break;
			case 'application/vnd.ms-excel':
				return array('xsl','xlsx');
				break;

			case 'application/vnd.ms-powerpoint':
				return array('ppt','pps','pptx');
				break;

			case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
				return array('xlsx');
				break;

			case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
				return array('pptx');
				break;

			case 'text/plain':
				return array('txt','text');
				break;

			default: return false;
		}

	}

  	function readZippedXML($archiveFile,$fileName) {
  		try {
			$arr_type = array(
			  'docx' => 'word/document.xml',
			  'xlsx' => 'xl/workbook.xml',
			  'pptx' => 'ppt/presentation.xml'
	 	 );
		  $name = explode('.', $fileName);
		  $typePath = "";
		  if(isset($arr_type[$name[count($name)-1]])){
			  $typePath = $arr_type[$name[count($name)-1]];
		  }
		  // lay tu http://www.freeformatter.com/mime-types-list.html
		  $arr_type_document = array(
			  'xl/workbook.xml' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			  'word/document.xml' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			  'ppt/presentation.xml' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
		  );
		  // Create new ZIP archive
		  $zip = new ZipArchive;
		  // Open received archive file
		  if (true === $zip->open($archiveFile)) {
			  	// If done, search for the data file in the archive
				// If found, read it to the string
				$data = $zip->getFromIndex(0);
				  // Close archive file
				$zip->close();
				// Load XML from a string
				// Skip errors and warnings
				try {
					if(empty($data))
						return '';
			  		$xml = DOMDocument::loadXML($data, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
				} catch (Exception $e) {
				}
				if(!empty($xml)){
				  // Return data without XML formatting tags
	  				$elem = $xml->getElementsByTagName('Override');
					foreach($elem as $e){
						$path = $e->getAttribute('PartName');
						$type = $e->getAttribute('ContentType');
						if(preg_match('/'.str_replace('/','\/',$typePath).'(.*)/i',$path)){
							if(preg_match('/'.str_replace('/','\/',$arr_type_document[$typePath]).'/i',$type)){
								return  $arr_type_document[$typePath];
							}
						}
					};
				}
		  }
  		} catch (Exception $e) {
  		}
  		return "";
	}
}